#include "../../Logs/superlog.h"

#include <unistd.h>
#include <cstring>
#include <mysql/mysql.h>

class DatabaseSink : public superlog::LogSink
{
public:
    DatabaseSink(MYSQL*& my, const std::string& tablename)
        : _my(my),
          _tablename(tablename)
    {
        mysql_set_character_set(_my, "utf8");
    }
    ~DatabaseSink() override
    {
        if (_my)
        {
            // 关闭MySQL连接
            mysql_close(_my);
        }
    }

    void log(const char* data, size_t len) override
    {
        // 使用预处理语句避免SQL注入
        std::string sql = "INSERT INTO " + _tablename + " (log_msg) VALUES (?);"; //?是一个占位符，表示待插入的实际数据

        // 创建预处理的句柄
        MYSQL_STMT* stmt = mysql_stmt_init(_my); //指向预处理语句的指针
        if (!stmt)
        {
            std::cerr << "mysql_stmt_init failed!" << std::endl;
            return;
        }

        // insert语句的预处理
        if (mysql_stmt_prepare(stmt, sql.c_str(), sql.size()) != 0)
        {
            std::cerr << "mysql_stmt_prepare failed: " << mysql_stmt_error(stmt) << std::endl;
            mysql_stmt_close(stmt);
            return;
        }

        // 初始化绑定参数结构体
        MYSQL_BIND bind[1];
        memset(bind, 0, sizeof(bind));
        // MYSQL_BIND结构的数组仅包含1个元素
        bind[0].buffer_type = MYSQL_TYPE_STRING;
        bind[0].buffer = (char*)data;
        bind[0].buffer_length = len;

        if (mysql_stmt_bind_param(stmt, bind) != 0)
        {
            std::cerr << "mysql_stmt_bind_param() failed: " << mysql_stmt_error(stmt) << std::endl;
            mysql_stmt_close(stmt);
            return;
        }

        // 执行预处理语句
        if (mysql_stmt_execute(stmt) != 0)
        {
            std::cerr << "mysql_stmt_execute() failed: " << mysql_stmt_error(stmt) << std::endl;
        }
        mysql_stmt_close(stmt); //关闭预处理语句
    }
private:
    MYSQL* _my; //指向数据库连接对象的指针
    std::string _tablename; //数据库表名
};

int main()
{
    const std::string host = "127.0.0.1";       //数据库服务器的地址
    const std::string user = "workbench";       //数据库用户名
    const std::string passwd = "123456";        //数据库密码
    const std::string database = "superlog";    //数据库名称
    const unsigned int port = 3306;             //数据库服务器端口号
    const std::string tablename = "logs";       //数据库表名

    // 验证库是否引入成功(查看当前MySQL客户端的版本号)
    std::cout << "********************  " << "[MySQL Client Version: " << mysql_get_client_info() << "]  ********************" << std::endl;
    MYSQL* my(mysql_init(nullptr));
    if(nullptr == my)
    {
        std::cerr << "Init MYSQL Error!" << std::endl;
        return 1;
    }
    if(mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), database.c_str(), port, nullptr, 0) == nullptr)
    {
        std::cerr << "Connect MySQL Error!" << mysql_error(my) << std::endl;
        mysql_close(my);
        return 1;
    }
    std::cout << "Connect Success!" << std::endl;

    std::unique_ptr<superlog::LoggerBuilder> builder(new superlog::GlobalLoggerBuilder());
    builder->buildLoggerName("sync_logger"); //同步日志
    builder->buildFormatter("[%c][%f:%l]%m");
    builder->buildLoggerType(superlog::LoggerType::LOGGER_SYNC);
    builder->buildSink<DatabaseSink>(my, tablename);
    superlog::Logger::ptr logger = builder->build();

    size_t cur = superlog::util::Date::now();
    while(superlog::util::Date::now() < cur + 2)
    {
        logger->fatal("这是一条测试日志");
        usleep(1000); //1ms写一条日志
    }

    // 执行一个查询语句来获取结果
    std::string sql = "SELECT * FROM " + tablename + ";";
    if (mysql_query(my, sql.c_str()) != 0)
    {
        std::cerr << "Query failed. Error: " << mysql_error(my) << std::endl;
        mysql_close(my);
        return 1;
    }

    // 获取执行结果
    MYSQL_RES *res = mysql_store_result(my);
    if(nullptr == res)
    {
        std::cerr << "mysql_store_result error: " << mysql_error(my) << std::endl;
        mysql_close(my);
        return 1;
    }

    // 打印结果行数和列数
    int rows = mysql_num_rows(res);
    int fields = mysql_num_fields(res);
    std::cout << "[行: " << rows << ", 列: " << fields << "]" << std::endl;                                                                                                                        

    // 打印属性
    MYSQL_FIELD *fields_array = mysql_fetch_fields(res); //获取列名
    std::cout << "[Database: " << fields_array[0].db << ", Table: " << fields_array[0].table << "]\n" <<std::endl;
    for(int i = 0; i < fields; i++)
    {
        std::cout << fields_array[i].name << "\t\t";
    }
    std::cout << std::endl;

    // 打印日志内容
    for(int i = 0; i < rows; i++)
    {
        // 获取结果内容
        MYSQL_ROW row = mysql_fetch_row(res); //MYSQL_ROW其实就是char**，可以把它当作一个二维数组来用
        for(int j = 0; j < fields; j++)
        {
            // mysql将所有的数据读取出来时，全都当作字符串
            if(row[j] == nullptr) std::cout << "NULL" << "\t"; //处理可能出现的NULL值，避免访问空指针
            else std::cout << row[j] << "\t"; // *(row+j)
        }
        std::cout << std::endl;
    }
    std::cout << "\n****************************  " << "[日志显示完毕]" << "  ****************************" << std::endl;
    mysql_free_result(res); //释放结果集的空间

    return 0;
}